import { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import api from '../api';
import issuesApi from '@/apps/dashboard/pages/issues/api';
import GithubSimpleTable from "@/apps/dashboard/components/tables/GithubSimpleTable";
import SpinnerCentered from "@/apps/dashboard/components/progress/SpinnerCentered";
import { CellType } from "@/apps/dashboard/components/tables/rows/GithubRow"
import { Avatar, Badge, Box, Button, DataTable, Divider, Frame, HorizontalGrid, HorizontalStack, Link, Text, TopBar, VerticalStack } from '@shopify/polaris';
import './styles.css'
import func from "@/util/func";
import IssuesGraph from './IssuesGraph';
import ReportSummaryInfoCard from './ReportSummaryInfoCard';
import Category from './Category';
import ReportRecommendations from './ReportRecommendations';
import ReportTOC from './ReportTOC';

function VulnerabilityReport() {
    const pdfRef = useRef();

    const params = useParams();
    const testingRunSummaryId = params.summaryId
    const issuesFilter = params.issuesFilter

    const [loading, setLoading] = useState(true)
    const [pdfDownloadEnabled, setPdfDownloadEnabled] = useState(false)

    const [severitiesCount, setSeveritiesCount] = useState({ HIGH: 0, MEDIUM: 0, LOW: 0 });
    const [categoryVsIssuesMap, setCategoryVsIssuesMap] = useState({})
    const [categoryVsApisCountMap, setCategoryVsApisCountMap] = useState({})
    const [aktoFindingsTableData, setAktoFindingsTableData] = useState([])
    const [ aktoRecommendations, setAktoRecommendations ] = useState([])

    const [subCategoryMap,setSubCategoryMap] = useState({})
    const [categoryMap, setCategoryMap] = useState({})

    const organizationName = func.capitalizeFirstLetter(window.ACCOUNT_NAME || "")
    const currentDate = func.formatReportDate(new Date());

    const createVulnerabilityMap = (testingRunResults, categoryMap, subCategoryMap) => {
        let categoryVsIssuesMap = {}
        let categoryVsApisCountMap = {}
        let issueVsVulMap = {}
        let aktoFindingsTableData = []
        let aktoRecommendationsData = []
        let high = 0
        let medium = 0
        let low = 0
        testingRunResults?.length > 0 && testingRunResults.forEach((testingRun) => {
            let testSubtype = testingRun?.testSubType
            let testInfo = subCategoryMap?.[testSubtype]

            if (!testInfo) {
                return
            }
            let severity = testInfo?.superCategory?.severity?._name
            let severityIndex = 0;
            switch (severity) {
                case 'HIGH':
                    ++high
                    severityIndex = 2
                    break;
                case 'MEDIUM':
                    ++medium
                    severityIndex = 1
                    break;
                case 'LOW':
                    ++low
                    severityIndex = 0
                    break;
                default:
                    break;
            }

            let vulnerabilities = issueVsVulMap[testSubtype]
            if (vulnerabilities === undefined) {
                vulnerabilities = JSON.parse(JSON.stringify(testInfo))
            }
            let vulnerableTestingRunResults = vulnerabilities["vulnerableTestingRunResults"]
            if (vulnerableTestingRunResults === undefined) {
                vulnerableTestingRunResults = []
            }
            vulnerableTestingRunResults.push(testingRun)
            vulnerabilities['vulnerableTestingRunResults'] = vulnerableTestingRunResults
            vulnerabilities['severityIndex'] = severityIndex
            issueVsVulMap[testSubtype] = vulnerabilities
        })
        setSeveritiesCount({ HIGH: high, MEDIUM: medium, LOW: low });
        let severityMapForIssueCategory = {}
        for (const [testSubType, issue] of Object.entries(issueVsVulMap)) {
            const categoryName = issue.superCategory.name
            const severity = issue?.superCategory?.severity?._name

            if (!categoryName) {
                continue
            }

            let issuesList
            if (!categoryVsIssuesMap.hasOwnProperty(categoryName)) {
                issuesList = []
                categoryVsIssuesMap[categoryName] = issuesList
            } else {
                issuesList = categoryVsIssuesMap[categoryName]
            }

            issuesList.push(issue)

            let apisCount = 0
            if (!categoryVsApisCountMap.hasOwnProperty(categoryName)) {
                categoryVsApisCountMap[categoryName] = 0
            } else {
                apisCount = categoryVsApisCountMap[categoryName]
            }

            if(severityMapForIssueCategory.hasOwnProperty(categoryName)){
                let initialCount = severityMapForIssueCategory[categoryName][severity] || 0
                severityMapForIssueCategory[categoryName][severity] = initialCount + 1
            }else{
                severityMapForIssueCategory[categoryName] = {}
                severityMapForIssueCategory[categoryName][severity] = 1 
            }

            apisCount += issue.vulnerableTestingRunResults.length
            categoryVsApisCountMap[categoryName] = apisCount

            //Add to akto recommendation
            if (issue.hasOwnProperty("remediation")) {
                aktoRecommendationsData.push({
                    title: issue.testName,
                    content:  issue.remediation
                })
            }
        }


        Object.keys(categoryVsIssuesMap).forEach((categoryName, index) => {
            const categoryDetails = categoryMap[categoryName]

            aktoFindingsTableData.push({
                key: index,
                sno: index + 1,
                categoryDisplayName: categoryDetails.displayName,
                categoryDisplayNameComp: (
                    <Link onClick={(e) => e.stopPropagation()} url={`#${categoryName}`} removeUnderline>
                        <Text variant='bodyMd' color='subdued'>
                            {categoryDetails.displayName}
                        </Text>
                    </Link>
                ),
                apisAffected: categoryVsApisCountMap[categoryName],
                categorySeverity: severityMapForIssueCategory[categoryName] && severityMapForIssueCategory[categoryName] !== undefined ? 
                    Object.keys((severityMapForIssueCategory[categoryName])).map((x) => {
                        return severityMapForIssueCategory[categoryName][x] + " " + x + " "
                    }) : "-",
                categorySeverityComp: <Badge size="small" status={func.getTestResultStatus(categoryDetails.severity._name)}>{categoryDetails.severity._name}</Badge>
            })
        })

        const AKTO_RECOMMENDATIONS_LIMIT = 10
        if (aktoRecommendationsData.length > AKTO_RECOMMENDATIONS_LIMIT) {
            aktoRecommendationsData = aktoRecommendationsData.slice(0, AKTO_RECOMMENDATIONS_LIMIT)
        }
        

        setSubCategoryMap(subCategoryMap)
        setCategoryMap(categoryMap)
        setCategoryVsIssuesMap(categoryVsIssuesMap)
        setCategoryVsApisCountMap(categoryVsApisCountMap)
        setAktoFindingsTableData(aktoFindingsTableData)
        setAktoRecommendations(aktoRecommendationsData)
    }

    const fetchVulnerableData = async () => {
        let resultsCount = 0;
        let vulnerableTestingRunResults = []
        //let sampleDataVsCurlMap = {}

        if (testingRunSummaryId) {
            while (true) {
                let testingRunCountsFromDB = 0
                await api.fetchVulnerableTestingRunResults(testingRunSummaryId, resultsCount).then((resp) => {
                    vulnerableTestingRunResults = [...vulnerableTestingRunResults, ...resp.testingRunResults]
                    testingRunCountsFromDB = resp.testingRunResults.length
                    //sampleDataVsCurlMap = { ...sampleDataVsCurlMap, ...resp.sampleDataVsCurlMap }
                })
                resultsCount += 50
                if (testingRunCountsFromDB < 50) {
                    //EOF: break as no further documents exists
                    break
                }
            }
        } else if (issuesFilter) {
            while (true) {
                let testingRunCountsFromDB = 0
                let filters = JSON.parse(atob(issuesFilter))
                await issuesApi.fetchVulnerableTestingRunResultsFromIssues(filters, resultsCount).then(resp => {
                    vulnerableTestingRunResults = [...vulnerableTestingRunResults, ...resp.testingRunResults]
                    testingRunCountsFromDB = resp.totalIssuesCount
                    //sampleDataVsCurlMap = { ...sampleDataVsCurlMap, ...resp.sampleDataVsCurlMap }
                })
                resultsCount += 50
                if (testingRunCountsFromDB < 50 || resultsCount >= 1000) {
                    //EOF: break as no further documents exists
                    break
                }
            }
        }
        //setDataToCurlObj(sampleDataVsCurlMap)

        const resp = await api.fetchAllSubCategories(true, "runTests");
        let subCategoryMap = {};
        resp.subCategories.forEach((x) => {
            subCategoryMap[x.name] = x;
        });
        let categoryMap = {};
        resp.categories.forEach((category) => {
            categoryMap[category.name] = category;
        });

        createVulnerabilityMap(vulnerableTestingRunResults, categoryMap, subCategoryMap)
    }

    useEffect(() => {
        setLoading(true)
        setPdfDownloadEnabled(false)
        fetchVulnerableData()
        setLoading(false)
        setPdfDownloadEnabled(true)
    }, [])

    

    const handleDownloadPF = async () => {
        const WAIT_DURATION = 2500, MAX_RETRIES = 15
        const reportUrl = window.location.href
        
        let pdfError = ""
        let status
        let pdf

        setPdfDownloadEnabled(false)

        const progressToastInterval = setInterval(() => {
            func.setToast(true, false, "Report PDF generation in progress. Please wait...")
        }, 1000)

        try {
            // Trigger pdf download
            const startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl)
            const reportId = startDownloadReponse?.reportId
            status = startDownloadReponse?.status

            if (reportId !== null && status === "IN_PROGRESS") {
                // Poll for PDF completion
                for(let i = 0; i < MAX_RETRIES; i++) {
                    const pdfPollResponse = await api.downloadReportPDF(reportId, organizationName, currentDate, reportUrl)
                    status = pdfPollResponse?.status

                    if (status === "COMPLETED") {
                        pdf = pdfPollResponse?.pdf
                        break
                    } else if (status === "ERROR") {
                        pdfError = "Failed to download PDF"
                        break
                    }

                    await func.sleep(WAIT_DURATION)

                    func.setToast(true, false, "Report PDF generation in progress. Please wait...")
                }
            } else {
                pdfError = "Failed to start PDF download"
            }
        } catch (err) {
            pdfError = err.message
        }

        clearInterval(progressToastInterval)

        if (status === "COMPLETED") {
            if (pdf === undefined) {
                pdfError = "Failed to download PDF"
            }
            else {
                // Download the PDF
                try {
                    const byteCharacters = atob(pdf);
                    const byteNumbers = new Array(byteCharacters.length);
                    for (let i = 0; i < byteCharacters.length; i++) {
                        byteNumbers[i] = byteCharacters.charCodeAt(i);
                    }
                    const byteArray = new Uint8Array(byteNumbers);
                    const blob = new Blob([byteArray], { type: "application/pdf" });
                    const link = document.createElement("a");
                    link.href = window.URL.createObjectURL(blob);
                    link.setAttribute("download", "akto_security_findings.pdf");
                    document.body.appendChild(link);
                    link.click();
                    func.setToast(true, false, "Report PDF downloaded.")
                } catch (err) {
                    pdfError = err.message
                }
            }  
        }
        
        if (pdfError !== "") {
            func.setToast(true, true, `Error while downloading PDF. Please try again. \nError: ${pdfError}`)
        } 

        setPdfDownloadEnabled(true)
    }
    
    const reportSecondaryMenu = (
        <div className="report-header-css header-css" id="report-secondary-menu-container">
            <Box width="100%">
                <HorizontalStack align="space-between">
                    <VerticalStack>
                        <Text variant="headingXs">{organizationName} API Security Findings</Text>
                        <Text variant="bodySm">{currentDate}</Text>
                    </VerticalStack>
                    <div style={{ display: 'flex', alignItems: 'center' }}>
                        {/* <Button primary onClick={() => handleDownloadPF()} disabled={!pdfDownloadEnabled}>Download</Button> */}
                        <img src='/public/white_logo.svg' alt="Logo" className='top-bar-logo' />
                    </div>
                </HorizontalStack>
            </Box>
        </div>
    )

    // Table of contents
    const aktoFindingsChildren = []
    Object.keys(categoryVsIssuesMap).forEach((categoryName, index) => {
        const categoryDetails = categoryMap[categoryName]
        aktoFindingsChildren.push({
            text: categoryDetails.displayName,
            link: categoryName,
            children: null,
        })
    })
    
    const tocList = [
        { text: "Report summary", link: "report-summary", children: null },
        { text: "Akto findings", link: "findings-table", children: aktoFindingsChildren },
        { text: "Our Approach: Scanning using Swagger file vs Real traffic data", link: "akto-approach", children: null },
    ]

    if (aktoRecommendations.length > 1) {
        tocList.push({ text: "Akto recommendations", link: "akto-recommendations", children: null})
    }


    // Akto findings table
    const aktoFindingsTableResourceName = {
        singular: 'Akto finding',
        plural: 'Akto findings',
    };

    const aktoFindingsTableHeaders = [
        {
            title: "S.No.",
            value: "sno",
            type: CellType.TEXT
        },
        {
            title: "Issue catgory",
            value: "categoryDisplayNameComp",
            textValue: "categoryDisplayName",
        },
        {
            title: "APIs affcted",
            value: "apisAffected",
            type: CellType.TEXT
        },
        {
            title: "Severity",
            value: "categorySeverity",
            type: CellType.TEXT
        }
    ]

    const reportTopBar = (
        <TopBar secondaryMenu={reportSecondaryMenu} />
    )

    // Report summary
    let totalIssues = 0, apisAffected = 0
    Object.values(categoryVsIssuesMap).forEach(categoryIssuesList => {
        totalIssues += categoryIssuesList.length
    })
    Object.values(categoryVsApisCountMap).forEach(apisCount => {
        apisAffected += apisCount
    })

    const reportSummaryItems = [
        {
            title: "Total issues",
            data: totalIssues,
        },
        {
            title: "Severity",
            data: (
                <Box>
                    <HorizontalGrid columns={3} gap={1}>
                        <Box key="high" borderInlineEndWidth={1} paddingBlockStart={1} paddingBlockEnd={1} borderColor="border-subdued">
                            <div style={{ color: "#D72C0D" }}>
                                <Text variant="headingLg">{severitiesCount?.HIGH} High</Text>
                            </div>
                        </Box>
                        <Box key="medium" borderInlineEndWidth={1} paddingBlockStart={1} paddingBlockEnd={1} borderColor="border-subdued">
                            <div style={{ color: "#916A00" }}>
                                <Text variant="headingLg">{severitiesCount?.MEDIUM} Medium</Text>
                            </div>
                        </Box>
                        <Box key="low" borderInlineEndWidth={0} paddingBlockStart={1} paddingBlockEnd={1} borderColor="border-subdued">
                            <div style={{ color: "#2C6ECB" }}>
                                <Text variant="headingLg">{severitiesCount?.LOW} Low</Text>
                            </div>
                        </Box>
                    </HorizontalGrid>
                </Box>


            ),
            isComp: true
        },
        {
            title: "APIs affected",
            data: apisAffected
        }
    ]

    // Approach
    const approachItems = [
        {
            title: "Enhanced Endpoint Coverage",
            content: "Even though Swagger files include several endpoints, only a small portion are directly testable. The majority require specific data elements like identifiers for testing. By utilizing real traffic data, we can significantly increase our endpoint coverage."
        },
        {
            title: "Expanded Test Scope",
            content: "Real traffic data allowed us to test for more sophisticated issues such as Business Logic flaws, Broken Authentication, and Broken Authorization, which isn't possible with the Swagger file alone."
        },
        {
            title: "Detection of Shadow APIs",
            content: "Our approach reveals several APIs that aren't documented in the Swagger file, highlighting the presence of Shadow APIs."
        }
    ]

    return (
        <div>
            <Frame topBar={reportTopBar}>
                {loading === false ?
                    (<div ref={pdfRef} id="report-container">
                        <Box background="bg">
                            <div className='report-cover-page-container'>
                                <img src='/public/vulnerability_header.png' alt="Header Image" className='report-cover-page' />
                                <img src='/public/white_logo.svg' alt='akt-logo' className='report-akto-logo'></img>
                                <div className='report-cover-page-content'>
                                    <Box width="100%">
                                        <HorizontalStack align="space-between">
                                            <VerticalStack gap={'5'}>
                                                <Text variant="bodyLg">Vulnerability Report</Text>
                                                <div className="heading-text">
                                                    <Text variant="heading4xl">{organizationName} API Security Findings</Text>
                                                </div>
                                                <Text variant="bodyMd">{currentDate}</Text>
                                            </VerticalStack>
                                        </HorizontalStack>
                                    </Box>
                                </div>
                            </div>

                            <ReportTOC tocList={tocList}/>
                            <Divider />

                            <Box id="report-summary" paddingBlockStart={6} paddingBlockEnd={10} paddingInlineStart={5} paddingInlineEnd={5}>
                                <VerticalStack gap="4">
                                    <Text variant="headingLg">1. Report summary</Text>
                                    <ReportSummaryInfoCard summaryItems={reportSummaryItems} />
                                    <IssuesGraph categoryIssues={categoryVsIssuesMap} />
                                </VerticalStack>
                            </Box>
                            <Divider />

                            <Box id="findings-table" paddingBlockStart={6} paddingBlockEnd={8} paddingInlineStart={5} paddingInlineEnd={5}>
                                <VerticalStack gap="4">
                                    <Text variant="headingLg">2. Akto findings</Text>
                                    <GithubSimpleTable
                                        key="table"
                                        data={aktoFindingsTableData}
                                        resourceName={aktoFindingsTableResourceName}
                                        headers={aktoFindingsTableHeaders}
                                        useNewRow={true}
                                        condensedHeight={true}
                                        hideQueryField={true}
                                        headings={aktoFindingsTableHeaders}
                                        hidePagination={true}
                                        showFooter={false}
                                    />
                                </VerticalStack>
                            </Box>
                            <Divider />

                            <Box>
                                {Object.keys(categoryVsIssuesMap).length > 0 && Object.keys(categoryVsIssuesMap).map((categoryName, index) => {
                                    return (
                                        <Box>
                                            <Category
                                                key={index}
                                                index={index}
                                                categoryName={categoryName}
                                                categoryIssues={categoryVsIssuesMap[categoryName]}
                                                categoryMap={categoryMap}
                                                categoryVsApisCountMap={categoryVsApisCountMap}
                                            />
                                            {index !== categoryVsIssuesMap.length - 1 ? <Divider />: null}
                                        </Box>
                                    )
                                })}
                                <div className="report-page-break"></div>
                            </Box>
                            
                            
                            <Box id="akto-approach" paddingBlockStart={6} paddingBlockEnd={8} paddingInlineStart={5} paddingInlineEnd={5}>
                                <VerticalStack gap="4">
                                    <Text variant="headingLg">3. Our Approach: Scanning using Swagger file vs Real traffic data</Text>
                                    <VerticalStack gap="3">
                                        <Text variant="bodyMd">
                                            Utilizing Akto's approach of using real traffic data for API scanning has numerous advantages for API issues:
                                        </Text>
                                        <VerticalStack gap={3}>
                                            {approachItems.map((item, index) => {
                                                return (
                                                    <Box key={index}>
                                                        <Text variant='bodySm'> <b>{index + 1}. {item.title}: </b>{item.content}</Text>
                                                    </Box>
                                                )
                                            })}
                                        </VerticalStack>
                                    </VerticalStack> 
                                </VerticalStack>
                            </Box>
                            
                            {aktoRecommendations.length !== 0 ?
                                <Box>
                                    <div className="report-page-break"></div>
                                    <Divider />
                                    <ReportRecommendations 
                                        title="Akto recommendations"
                                        sno="4"
                                        itemsList={aktoRecommendations}
                                    /> 
                                </Box>: null}
                            

                            <div className='report-end-image-container'>
                                <img src="/public/vulnerability_footer.png" alt="Footer Image" className='report-end-image' />
                                <div className='report-end-image-text'>The end.</div>
                            </div>
                        </Box>
                    </div>
                    ) : <SpinnerCentered />}
            </Frame>
        </div>
    )

}

export default VulnerabilityReport;